/*************************************************************************
    > File Name: test.c
    > Author: Xing.Dai
    > Mail: Xing.Dai@aliyun.com 
    > Created Time: 2018-02-24 14:20
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#define BUF_SIZE	1024
#define DEVICE_FILE	"/dev/scull"

#define TEST_ITEM(cmd, func) {cmd, (#cmd), func}
//typedef int (*test_func)(int, char **);

typedef struct {
	unsigned int id;	/* Test id */
	const char *cmd;	/* string  */
	int (*func)(int, char **);  /* Fucntion */
}test_table;

/**
 * @Synopsis test id.  
 */
enum test_id_list{
	TEST_LSEEK,
	TEST_ASYNC,
	TEST_ITEM_MAX
};

/*************** All function declaration.********************/
int test_lseek(int , char **);
int test_async(int , char **);

const test_table test_table_item[] = 
{
	TEST_ITEM(TEST_LSEEK, test_lseek),
	TEST_ITEM(TEST_ASYNC, test_async),
};

/**
 * @Synopsis  Choose test item in id.
 *
 * @Returns   
 */
int main(int argc, char **argv)
{
	int ret = 0;
	int count = 0;
	int test_item = TEST_ITEM_MAX;

	printf("Please choose test func:\n");
	for(count = 0; count < TEST_ITEM_MAX; count++) 	{
		printf("[%d]: %s.\n", test_table_item[count].id, test_table_item[count].cmd);
	}
	printf("Input test id: ");
	scanf("%d", &test_item);
	
	if(test_item >= TEST_ITEM_MAX)	{
		printf("Error id!\n");
		printf("Correct id: %d ~ %d\n", 0, TEST_ITEM_MAX - 1);
		return 0;
	}

	ret = test_table_item[test_item].func(argc, argv);

	return ret;
}

/**
 * @Synopsis  Device driver lseek test.
 *
 * @Returns   
 */
int test_lseek(int argc, char **argv)
{
	int fd;
	int num;
	char buf[BUF_SIZE];

	fd = open(DEVICE_FILE, O_RDWR);
	if(fd < 0)
	{
		printf("open scull error!\n");
		return -1;
	}

	memset(buf, 0, BUF_SIZE);
	num = read(fd, buf, BUF_SIZE);
	buf[num] = 0;
	printf("1: %s\n", buf);

	lseek(fd, 2, SEEK_SET);
	write(fd, "aa", 2);
	memset(buf, 0, BUF_SIZE);
	num = read(fd, buf, BUF_SIZE);
	buf[num] = 0;
	printf("2: %s\n", buf);
	
	lseek(fd, 2, SEEK_SET);
	num = read(fd, buf, BUF_SIZE);
	buf[num] = 0;
	printf("3: %s\n", buf);
	
	lseek(fd, 0, SEEK_SET);
	lseek(fd, 2, SEEK_CUR);
	num = read(fd, buf, BUF_SIZE);
	buf[num] = 0;
	printf("4: %s\n", buf);

	lseek(fd, 0, SEEK_SET);
	lseek(fd, 0, SEEK_END);
	memset(buf, 0, BUF_SIZE);
	printf("read return value is %zd.\n", read(fd, buf, BUF_SIZE));

	return 0;
}

/* Test for async. */
int gotdata = 0;
void sighandler(int signo)
{
	printf("%s: %d.\n", __func__, signo);
	if (signo == SIGIO)
		gotdata++;
	return;
}

char buffer[4096];

/**
 * @Synopsis  
 *
 * @Param argc
 * @Param argv
 *
 * @Returns   
 */
int test_async(int argc, char **argv)
{
	int fd;
	int count;
	struct sigaction action;

	fd = open("/dev/scullpipe", O_RDWR);
	dup2(fd, STDIN_FILENO);

	memset(&action, 0, sizeof(action));
	action.sa_handler = sighandler;
	action.sa_flags = 0;
	sigaction(SIGIO, &action, NULL);

	fcntl(STDIN_FILENO, F_SETOWN, getpid());
	fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);

	while(1) {
		/* this only returns if a signal arrives. */
		sleep(97400); /* one day */
		if (!gotdata)
			continue;
		
		count = read(0, buffer, 4096);

		/* bug: if avail data is more than 4k ...*/
		write(1, buffer, count);
		gotdata = 0;
	}
}
